package com.github.binarywang.demo.wx.mp.controller;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.github.binarywang.demo.wx.mp.config.WxMpConfiguration;
import com.google.common.collect.Maps;

import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.URIUtil;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;

/**
 * @author Edward
 */
@Controller
@RequestMapping("/wx/redirect/{appid}/")
public class WxRedirectController {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	private HttpServletRequest request; //spring 会自动 注入。 线程安全。
//	@Autowired
//	private HttpServletResponse response; // 不可用spring的注入方式，会报错。

	private String getBasePath() {
		return request.getScheme() + "://" + request.getServerName()
				+ (request.getServerPort() == 80 ? "" : ":" + request.getServerPort()) + request.getContextPath() + "/";
	}

	@RequestMapping("greet")
	public String greetUser(@PathVariable String appid, @RequestParam String code, ModelMap map) {

		WxMpService mpService = WxMpConfiguration.getMpServices().get(appid);

		try {
			WxMpOAuth2AccessToken accessToken = mpService.oauth2getAccessToken(code);
			WxMpUser user = mpService.oauth2getUserInfo(accessToken, null);
			map.put("user", user);
		} catch (WxErrorException e) {
			e.printStackTrace();
		}

		return "greet_user";
	}
	
	//==================================== 静默获取用户openid ==========================================

	/**
	 * 第一步：用户同意授权，获取code。此处为静默授权，不需要用户点击授权 静默授权 scope 值为 snsapi_base
	 * 
	 * 测试链接：http://1f37d952.ngrok.io/wx/redirect/wx1353a0df33e07c8c/getOpenid?
	 * toUrl=http://1f37d952.ngrok.io/wx/redirect/wx1353a0df33e07c8c/testToUrl/@from=postman@to=heheda
	 * 或：http://1f37d952.ngrok.io/wx/redirect/wx1353a0df33e07c8c/getOpenid?
	 * toUrl=http%3a%2f%2f1f37d952.ngrok.io%2fwx%2fredirect%2fwx1353a0df33e07c8c%2ftestToUrl%2f%3ffrom%3dpostman%26to%3dheheda
	 * 
	 * @param appid
	 * @param toUrl
	 *            前端最好需要对参数处理，参数之间用@符号进行链接，或者对 toUrl进行UrlEncode。
	 * @param request
	 */
	@RequestMapping("getOpenid")
	public void getOpenid(@PathVariable String appid, @RequestParam(value = "toUrl", required = true) String toUrl, HttpServletResponse response) {

		WxMpService mpService = WxMpConfiguration.getMpServices().get(appid);
		
		// eg:http://lese.tech/test/?user=zhangsan&sex=1
		// 替换后：http://lese.tech/test/@use=zhangsan@sex=1
		toUrl = toUrl.replace("?", "@").replace("&", "@");
		String redirectUrl = this.getBasePath() + "wx/redirect/" + appid + "/getOpenidByCode/?toUrl=" + toUrl;
		// redirectUrl 无须UrlEncode，方法内会进行encode
		String authUrl = mpService.oauth2buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_BASE, null);
		
		if (logger.isDebugEnabled()) {
			logger.debug("basePath = {}", this.getBasePath());// basePath:http://localhost:8683/WebDemo/
			logger.debug("redirectUrl = {}", redirectUrl);
			logger.debug("authUrl = {}", authUrl);
		}
		
		try {
			response.sendRedirect(authUrl);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * redirect_uri/?code=CODE&state=STATE。
	 * 
	 * @param appid
	 * @param toUrl
	 *            跳转的目标地址。url中的参数用@符号链接。eg：http://lese.tech/test/@use=zhangsan@sex=1
	 * @param code
	 *            用户授权后微信返回的code
	 * @param request
	 */
	@RequestMapping("getOpenidByCode")
	public void getOpenidByCode(@PathVariable String appid,
			@RequestParam(value = "toUrl", required = true) String toUrl, String code, HttpServletRequest request,
			HttpServletResponse response) {
		if (logger.isDebugEnabled()) {
			logger.debug("queryParam = {}", request.getQueryString());
		}
		WxMpService mpService = WxMpConfiguration.getMpServices().get(appid);
		try {
			// 第二步：通过code换取网页授权access_token。accessToken中包含用户openid，静默授权到此即可。
			WxMpOAuth2AccessToken accessToken = mpService.oauth2getAccessToken(code);
			toUrl = toUrl.replaceFirst("@", "?").replace("@", "&");
			toUrl += toUrl.contains("?") ? "&openid=" + accessToken.getOpenId() : "?openid=" + accessToken.getOpenId();
			
			Cookie cookie = new Cookie("openid", accessToken.getOpenId());
			cookie.setPath("/");
			response.addCookie(cookie);
			
			try {
				response.sendRedirect(toUrl);
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (WxErrorException e) {
			e.printStackTrace();
		}
	}

	

	// ================================ 须用户授权，获取用户信息 ===================================
	
	/**
	 * 注意：在点击页面进入时即请求授权，如果用户拒绝，则将会退出页面，不会进入目标页面。
	 * 
	 * 测试链接：http://1f37d952.ngrok.io/wx/redirect/wx1353a0df33e07c8c/getUserInfo?
	 * toUrl=http://1f37d952.ngrok.io/wx/redirect/wx1353a0df33e07c8c/testToUrl/@from=postman2@to=heheda2
	 * 或：http://1f37d952.ngrok.io/wx/redirect/wx1353a0df33e07c8c/getUserInfo?
	 * http%3a%2f%2f1f37d952.ngrok.io%2fwx%2fredirect%2fwx1353a0df33e07c8c%2ftestToUrl%2f%3ffrom%3dpostman%26to%3dheheda
	 * @param appid
	 * @param toUrl 前端最好需要对参数处理，参数之间用@符号进行链接，或者对 toUrl进行UrlEncode。
	 * @param response
	 */
	@RequestMapping("getUserInfo")
	public void getUserInfo(@PathVariable String appid, @RequestParam(value = "toUrl", required = true) String toUrl,
			HttpServletResponse response) {
		
		WxMpService mpService = WxMpConfiguration.getMpServices().get(appid);
		
		toUrl = toUrl.replace("?", "@").replace("&", "@");
		String redirectUrl = this.getBasePath() + "wx/redirect/" + appid + "/getUserInfoByCode/?toUrl=" + toUrl;
		String authUrl = mpService.oauth2buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, null);
		
		if (logger.isDebugEnabled()) {
			logger.debug("redirectUrl = {}", redirectUrl);
			logger.debug("authUrl = {}", authUrl);
		}
		try {
			response.sendRedirect(authUrl);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 注意：userInfo 的值做了URLEncoder.encode("userInfoJson", "utf-8")处理，获取值时应先解码在转json。
	 * 
	 * @param appid
	 * @param toUrl 跳转的目标地址。url中的参数用@符号链接。eg：http://lese.tech/test/@use=zhangsan@sex=1
	 * @param code
	 * @param response
	 */
	@RequestMapping("getUserInfoByCode")
	public void getUserInfoByCode(@PathVariable String appid,
			@RequestParam(value = "toUrl", required = true) String toUrl, String code, HttpServletResponse response) {
		WxMpService mpService = WxMpConfiguration.getMpServices().get(appid);
		try {
			// 第二步：通过code换取网页授权access_token。
			WxMpOAuth2AccessToken accessToken = mpService.oauth2getAccessToken(code);
			// 第三步：通过accessToken获取用户信息
			WxMpUser user = mpService.oauth2getUserInfo(accessToken, null);
			
			// userInfo转json字符串并URL.encode处理。
			String userJsonStr = URIUtil.encodeURIComponent(user.toString());
			
			toUrl = toUrl.replaceFirst("@", "?").replace("@", "&");
			toUrl += toUrl.contains("?") ? "&userInfo=" + userJsonStr : "?userInfo=" + userJsonStr;
			
			Cookie cookie = new Cookie("userInfo", userJsonStr);
			cookie.setPath("/");
			response.addCookie(cookie);
			
			try {
				response.sendRedirect(toUrl);
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (WxErrorException e) {
			e.printStackTrace();
		}
	}
	
	
	//=====================================  以下为测试接口 ===============================================
	
	@RequestMapping("testToUrl")
	@ResponseBody
	public Map<String, String> testToUrl(String from, String to, String openid, HttpServletRequest request) {
		if (logger.isDebugEnabled()) {
			logger.debug("[TestToUrl:QueryString = {}]", request.getQueryString());
		}
		Cookie[] cookies = request.getCookies();
		if (null != cookies && cookies.length != 0) {
			for (int i = 0; i < cookies.length; i++) {
				System.out.println("cookie_" + i + "_" + cookies[i].getName() + ":" + cookies[i].getValue());
				if (cookies[i].getName().equals("userInfo")) {
					try {
						String userInfoJsonStr = URLDecoder.decode(cookies[i].getValue(), "utf-8");
						System.out.println("userInfoJsonStr =" + userInfoJsonStr);
					} catch (UnsupportedEncodingException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		System.out.println("====done====");
		Map<String, String> result = Maps.newHashMap();
		result.put("openid", openid);
		result.put("from", from);
		result.put("to", to);
		return result;
	}

}
